#include "./Aliyun_TDDNS_Client.h"
char AliyunClient::b64chars[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";


AliyunClient::AliyunClient(String secretId,String secretKey,WiFiClient *wc, String subDomain){
     {
        API_HOST = "https://alidns.aliyuncs.com";
        secret_id = secretId;
        secret_key = secretKey;
        wifiClient = wc;
        domain = subDomain;
    }
}
String AliyunClient::getCurrentIp()
{
    HTTPClient http;
    String response = "";
    http.begin(*wifiClient, "http://ip.3322.net/"); // HTTP请求地址
    int httpCode = http.GET();
    if (httpCode > 0)
    { //断送有无返回值
        if (httpCode == HTTP_CODE_OK)
        { //判断请求是正确
            response = http.getString();
            response.trim();
            return response;
            // Serial.println(ip);
        }
    }
    else
    { //如果请求不成功，打印出错误信息
        Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
    }
    http.end();
    return response;
}
void AliyunClient::getCurrentTimeAndNonce(String &timeS, String &nonce)
{
    DateTime.setServer("ntp.aliyun.com");
    DateTime.setTimeZone("UTC");
    DateTime.begin();
    if (!DateTime.isTimeValid())
    {
        Serial.println("Failed to get time from server.");
    }
    else
    {
        String temp = DateTime.formatUTC(DateFormatter::ISO8601_);
        timeS = aliUrlEncode(temp);
        // Serial.printf("UTC86  Time:   %s\n",
        //               aliUrlEncode(temp).c_str());

        nonce = String(DateTime.getTime());
        // time_t t = time(NULL);
        // Serial.printf("OS local:     %s", asctime(localtime(&t)));
        // Serial.printf("OS UTC:       %s", asctime(gmtime(&t)));
    }
}
bool AliyunClient::isNeedChange(String &currentIp, String &oldIp)
{

    String result = "";
    String nonce = "";
    String timestamp = "";
    getCurrentTimeAndNonce(timestamp, nonce);

    getRecordParams(secret_id, domain, nonce, timestamp, result);
    String readySignStr = "";
    getSignStr(result, readySignStr);
    String signature = "";
    sign(readySignStr, signature);
    String requestUrl =  API_HOST + "/?" + result + "&Signature=" + aliUrlEncode(signature);
    HTTPClient http;
    http.begin(*wifiClient, requestUrl); // HTTP请求地址
    int httpCode = http.GET();
    if (httpCode > 0)
    { //断送有无返回值
        if (httpCode == HTTP_CODE_OK)
        { //判断请求是正确
            String response = http.getString();
            Serial.println("query ddns record response is "+response);
            StaticJsonDocument<800> doc;

            DeserializationError error = deserializeJson(doc, response);

            if (error)
            {
                Serial.print(F("deserializeJson() failed: "));
                Serial.println(error.f_str());
                return false;
            }

            // int TotalCount = doc["TotalCount"]; // 1
            // const char* RequestId = doc["RequestId"]; // "8F8424A3-E117-51B0-9474-9DA28D9690C0"
            // int PageSize = doc["PageSize"]; // 20

            JsonObject DomainRecords_Record_0 = doc["DomainRecords"]["Record"][0];
            // const char* DomainRecords_Record_0_RR = DomainRecords_Record_0["RR"]; // "light"
            // const char* DomainRecords_Record_0_Line = DomainRecords_Record_0["Line"]; // "default"
            // const char* DomainRecords_Record_0_Status = DomainRecords_Record_0["Status"]; // "ENABLE"
            // bool DomainRecords_Record_0_Locked = DomainRecords_Record_0["Locked"]; // false
            // const char* DomainRecords_Record_0_Type = DomainRecords_Record_0["Type"]; // "A"
            // const char* DomainRecords_Record_0_DomainName = DomainRecords_Record_0["DomainName"]; // ""
            const char *DomainRecords_Record_0_Value = DomainRecords_Record_0["Value"]; // ""
            const char *DomainRecords_Record_0_RecordId = DomainRecords_Record_0["RecordId"];
            // int DomainRecords_Record_0_TTL = DomainRecords_Record_0["TTL"]; // 600
            // int DomainRecords_Record_0_Weight = DomainRecords_Record_0["Weight"]; // 1
            recordId = String(DomainRecords_Record_0_RecordId);
            oldIp = String(DomainRecords_Record_0_Value);

            // int PageNumber = doc["PageNumber"]; // 1
  
        }else{
            String response = http.getString();
            Serial.println("query ddns record response is "+response);
            return false;
        }
    }
    else
    { //如果请求不成功，打印出错误信息
        Serial.printf("[HTTP] GET... failed, error111111: %s\n", http.errorToString(httpCode).c_str());
    }
    http.end();
    if (currentIp.equals(oldIp))
    {

        return false;
    }
    else
    {
        return true;
    }
}

bool AliyunClient::updateRecord(String &currentIp){
String nonce = "";
  String timestamp = "";
  getCurrentTimeAndNonce(timestamp, nonce);
  int index = domain.indexOf(".");
 String sub = domain.substring(0, index);
  String result = "";
  getUpdateParams(secret_id, recordId, sub, nonce, timestamp, currentIp, result);

  String readySignStr = "";
  getSignStr(result, readySignStr);
  // Serial.println(readySignStr);
  String signature = "";
  sign(readySignStr, signature);
  String requestUrl = API_HOST + "/?" + result + "&Signature=" + aliUrlEncode(signature);
  // Serial.println(requestUrl);
  HTTPClient http;
 http.begin(*wifiClient, requestUrl); // HTTP请求地址
  int httpCode = http.GET();
  if (httpCode > 0)
  { //断送有无返回值
    if (httpCode == HTTP_CODE_OK)
    { //判断请求是正确
      String response = http.getString();

      Serial.println("updateDomain======"+response);
    }
  }
  // return true temporary
  return true;
}
// AccessKeyId=123123&Action=UpdateDomainRecord&Format=JSON&RR=a&RecordId=123123&SignatureMethod=HMAC-SHA1&SignatureNonce=573686930&SignatureVersion=1.0&Timestamp=2022-07-19T01%3A44%3A37.592Z&Type=A&Value=192.168.1.1&Version=2015-01-09
// GET&%2F&AccessKeyId%3D123123%26Action%3DUpdateDomainRecord%26Format%3DJSON%26RR%3Da%26RecordId%3D123123%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3D573686930%26SignatureVersion%3D1.0%26Timestamp%3D2022-07-19T01%253A44%253A37.592Z%26Type%3DA%26Value%3D192.168.1.1%26Version%3D2015-01-09
// T4ANRqGPO4Nmx6iu2HbA7tLxv8A=
void AliyunClient::getUpdateParams(String &AccessKeyId, String &recordId, String &subDomain, String &nonce, String &timestamp, String &ip, String &str)
{
    str = "AccessKeyId=";
    str += AccessKeyId;
    str += "&Action=UpdateDomainRecord&Format=JSON&RR=";
    str += subDomain;
    str += "&RecordId=";
    str += recordId;
    str += "&SignatureMethod=HMAC-SHA1&SignatureNonce=";
    str += nonce;
    str += "&SignatureVersion=1.0&Timestamp=";
    str += timestamp;
    str += "&Type=A&Value=";
    str += ip;
    str += "&Version=2015-01-09";

}

// canonicalizedQueryString AccessKeyId=123123&Action=DescribeSubDomainRecords&Format=JSON&SignatureMethod=HMAC-SHA1&SignatureNonce=430267205&SignatureVersion=1.0&SubDomain=a.foobar.com&Timestamp=2022-07-19T07%3A04%3A07.080Z&Version=2015-01-09
// stringToSign GET&%2F&AccessKeyId%3D123123%26Action%3DDescribeSubDomainRecords%26Format%3DJSON%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3D430267205%26SignatureVersion%3D1.0%26SubDomain%3Da.foobar.com%26Timestamp%3D2022-07-19T07%253A04%253A07.080Z%26Version%3D2015-01-09
// Signature LTKncP4kIrQqfUaHFfKLPeQDjq8=
void AliyunClient::getRecordParams(String &AccessKeyId, String &subDomain, String &nonce, String &timestamp, String &str)
{
    str = "AccessKeyId=";
    str += AccessKeyId;
    str += "&Action=DescribeSubDomainRecords&Format=JSON";
    str += "&SignatureMethod=HMAC-SHA1&SignatureNonce=";
    str += nonce;
    str += "&SignatureVersion=1.0&";
    str += "SubDomain=";
    str += subDomain;
    str += "&Timestamp=";
    str += timestamp;
    str += "&Version=2015-01-09";
}
void AliyunClient::getSignStr(String &content, String &signStr)
{
    signStr = "GET&%2F&" + aliUrlEncode(content);
}
String AliyunClient::aliUrlEncode(String &content)
{
    String result = urlEncode(content);
    result.replace("!", "%21");
    result.replace("'", "%27");
    result.replace("(", "%28");
    result.replace(")", "%29");
    result.replace("+", "%20");
    result.replace("*", "%2A");
    result.replace("%7E", "~");
    return result;
}

void AliyunClient::sign(String &content, String &sig)
{
String secretAli = secret_key+"&";
    Sha1.initHmac((uint8_t *)secretAli.c_str(), strlen(secretAli.c_str()));

    Sha1.print(content);
    memset(b64, 0, sizeof(b64));
    for (in = Sha1.resultHmac(), out = 0;; in += 3)
    { // octets to sextets
        b64[out++] = in[0] >> 2;
        b64[out++] = ((in[0] & 0x03) << 4) | (in[1] >> 4);
        if (out >= 26)
            break;
        b64[out++] = ((in[1] & 0x0f) << 2) | (in[2] >> 6);
        b64[out++] = in[2] & 0x3f;
    }
    b64[out] = (in[1] & 0x0f) << 2;
    // Remap sextets to base64 ASCII chars
    for (i = 0; i <= out; i++)
        b64[i] = pgm_read_byte(&b64chars[b64[i]]);
    b64[i++] = '=';
    b64[i++] = 0;
    sig = String(b64);
}
